package com.leesche.netdoll.communication.processor;

import java.util.Date;
import java.util.List;

import com.leesche.framework.core.concurrent.parallel.Event;
import com.leesche.framework.core.concurrent.parallel.SubscribeEvents;
import com.leesche.framework.core.json.GsonUtil;
import com.leesche.framework.core.lang.StringUtil;
import com.leesche.framework.platform.redis.HashCommand;
import com.leesche.framework.platform.redis.RedisManager;
import com.leesche.netdoll.communication.Settings.ProcessorConfig;
import com.leesche.netdoll.communication.Settings.RedisModule;
import com.leesche.netdoll.communication.entity.GameStatus;
import com.leesche.netdoll.communication.entity.GameStatusCode;
import com.leesche.netdoll.communication.event.SessionChangedEvent;
import com.leesche.netdoll.communication.event.SessionChangedEvent.SessionStatus;
import com.leesche.netdoll.communication.protocol.CommandMessage;
import com.leesche.netdoll.communication.protocol.FrameType;
import com.leesche.netdoll.communication.protocol.MessageType;
import com.leesche.netdoll.communication.protocol.SimpleMessage;
import com.leesche.netdoll.communication.protocol.StartCommandMessage;
import com.leesche.netdoll.communication.protocol.StatusMessage;

@SubscribeEvents({ CommandMessage.class, StatusMessage.class, SessionChangedEvent.class, StartCommandMessage.class })
public class GameStatusProcessor extends AbstractNetDollExecutableProcessor {

	@Override
	protected void doExecute(int taskIndex, int bufferIndex, List<Event> events) {

		for (Event event : events) {
			if (event instanceof SimpleMessage) {
				SimpleMessage sm = (SimpleMessage) event;

				MessageType type = MessageType.fromCode(sm.getCode());

				switch (type) {
				case RSP_START:
					logger.info("processing RSP_START message from device {}...", sm.deviceId());
					updateGame((StartCommandMessage) sm);
					break;

				case STATUS:
					logger.info("processing STATUS message from device {}...", sm.deviceId());
					updateGame((StatusMessage) sm);
					// updateDevice((StatusMessage) sm);
					// updateIdle((StatusMessage) sm);
					break;

				default:
					// do nothing
					break;
				}
			} else {
				logger.info("processing SESSION message from device {}...", ((SessionChangedEvent) event).deviceId());
				updateGame((SessionChangedEvent) event);
				updateDevice((SessionChangedEvent) event);
				// updateIdle((SessionChangedEvent) event);
			}
		}
	}

	private static void updateDevice(SessionChangedEvent event) {

		HashCommand redis = RedisManager.custom().getHashCommand(RedisModule.TERMINAL);

		redis.hSet(RedisModule.KEY_DS, event.deviceId(),
				event.getStatus() == SessionStatus.Alive ? "online" : "offline");

	}

	//////////////////////////////// Idle Device
	//////////////////////////////// Handler//////////////////////////////////

	// private static void updateIdle(final SessionChangedEvent event) {
	// SetCommand redis = RedisManager.custom().getSetCommand(RedisModule.TERMINAL);
	//
	// if (SessionStatus.Alive == event.getStatus()) {
	// redis.sadd(RedisModule.KEY_DEVICE_IDLE, event.deviceId());
	// } else {
	// redis.srem(RedisModule.KEY_DEVICE_IDLE, event.deviceId());
	// }
	// }

	// private static void updateIdle(final StatusMessage sm) {
	// SetCommand redis = RedisManager.custom().getSetCommand(RedisModule.TERMINAL);
	//
	// if (GameStatusCode.fromMachineCode(sm.getError()) >
	// GameStatusCode.GS_START_WITHOUT_PRIZE) {
	// redis.sadd(RedisModule.KEY_DEVICE_IDLE, sm.deviceId());
	// }
	// }

	//////////////////////////////// Device Status
	//////////////////////////////// Handler//////////////////////////////////

	// private static void updateDevice(final StatusMessage sm) {
	// DeviceStatus ds = restoreDeviceStatus(sm.deviceId());
	//
	// ds.setIdle(GameStatusCode.fromMachineCode(sm.getError()) >
	// GameStatusCode.GS_START_WITHOUT_PRIZE);
	//
	// resetDeviceStatus(ds);
	// }

	// private static void updateDevice(final SessionChangedEvent event) {
	// DeviceStatus ds = restoreDeviceStatus(event.deviceId());
	//
	// ds.setOnline(event.getStatus() == SessionStatus.Alive);
	//
	// resetDeviceStatus(ds);
	// }

	// private static void resetDeviceStatus(final DeviceStatus ds) {
	// HashCommand redis =
	// RedisManager.custom().getHashCommand(RedisModule.TERMINAL);
	//
	// redis.hSet(RedisModule.KEY_DS, ds.getDeviceId(), GsonUtil.toJson(ds));
	// }

	// private static DeviceStatus restoreDeviceStatus(String deviceId) {
	//
	// DeviceStatus ds = null;
	//
	// HashCommand redis =
	// RedisManager.custom().getHashCommand(RedisModule.TERMINAL);
	// String json = redis.hGet(RedisModule.KEY_DS, deviceId);
	//
	// if (StringUtil.isNotBlank(json)) {
	// ds = GsonUtil.toBean(json, DeviceStatus.class);
	// }
	//
	// if (ds == null) {
	// ds = new DeviceStatus(deviceId, false);
	// }
	//
	// return ds;
	// }

	//////////////////////////////// Game Status
	//////////////////////////////// Handler//////////////////////////////////

	private static void updateGame(final SessionChangedEvent event) {
		if (StringUtil.isNotBlank(event.deviceId()) && StringUtil.isNotBlank(event.gameId())) {
			if (event.getStatus() == SessionStatus.Dead) {

				GameStatus gs = restoreGameStatus(event.gameId());

				if (gs != null && gs.getStatus() != null
						&& gs.getStatus() < GameStatusCode.GS_END_WITHOUT_PRIZE_NORMAL) {

					gs.setStatus(GameStatusCode.GS_END_NET_EXCEPTION);
					gs.setEtime(new Date(System.currentTimeMillis()));

					resetGameStatus(gs);
				}
			}
		}
	}

	private static void updateGame(final StatusMessage sm) {

		GameStatus gs = restoreGameStatus(sm.gameId());

		gs.setStatus(GameStatusCode.fromMachineCode(sm.getError()));

		if (gs.getStatus() > GameStatusCode.GS_START_WITHOUT_PRIZE) {
			gs.setEtime(new Date(System.currentTimeMillis()));
		}

		logger.info("update game {} on device {} with STATUS code {}. ", sm.gameId(), sm.deviceId(),
				GameStatusCode.fromMachineCode(sm.getError()));

		resetGameStatus(gs);

	}

	private static void updateGame(final StartCommandMessage cm) {
		GameStatus gs = restoreGameStatus(cm.gameId());

		if (FrameType.ACCEPT.getCode() == cm.getType()) {
			gs.setStatus(GameStatusCode.fromMachineCode(cm.getStatus()));

			long stime = System.currentTimeMillis();
			long etime = stime + cm.getRestTime() * 1000;

			gs.setStime(new Date(stime));
			gs.setEtime(new Date(etime));

			logger.info("game {} on device {} is accepted to start.", gs.getGameId(), gs.getDeviceId());

		} else {
			gs.setStatus(GameStatusCode.GS_TRY_FAILED);
			gs.setStime(new Date(System.currentTimeMillis()));
			gs.setEtime(new Date(System.currentTimeMillis()));

			logger.info("game {} on device {} is denied to start.", gs.getGameId(), gs.getDeviceId());
		}

		resetGameStatus(gs);
	}

	private static void resetGameStatus(GameStatus gs) {
		HashCommand redis = RedisManager.custom().getHashCommand(RedisModule.TERMINAL);

		redis.hSet(GameStatus.location(gs.getGameId()), gs.getGameId(), GsonUtil.toJson(gs));
	}

	private static GameStatus restoreGameStatus(String gameId) {

		String key = GameStatus.location(gameId);

		HashCommand redis = RedisManager.custom().getHashCommand(RedisModule.TERMINAL);

		String json = redis.hGet(key, gameId);

		GameStatus gs = null;

		if (StringUtil.isNotBlank(json)) {
			gs = GsonUtil.toBean(json, GameStatus.class);
		}

		return gs;
	}

	@Override
	public int getBufferSize() {
		return ProcessorConfig.GS_BUFFER_SIZE;
	}

	@Override
	public int getMaxBufferConsume() {
		return ProcessorConfig.GS_BUFFER_MAX_CONSUME;
	}

	@Override
	protected int getThreadSize() {
		return ProcessorConfig.GS_THREAD;
	}
}
